/*
 * Shell.cpp
 *
 *  Created on: May 18, 2011
 *      Author: Stijn, Bart, Dave
 */

#include <iostream>
#include <string>

#include "BuiltInCommand.h"
#include "Rule.h"
#include "PipedCommand.h"

#include "Token.h"

#include "Shell.h"

Shell::Shell() {
	rules = new std::vector<Rule*>();
	runProgram = true;
	backgroundPids = new std::vector<int>();
}

Shell::~Shell() {
	for(std::vector<Rule*>::iterator rule = rules->begin(); rule != rules->end(); ++rule) {
		delete (*rule);
	}

	rules->erase(rules->begin(), rules->end());
	delete rules;

	delete backgroundPids;
}

void Shell::parseCommands() {
	bool stopParsing = false;
	bool nextTokenIsInput = false;
	bool nextTokenIsOutput = false;

	Token* currentToken;
	Rule* currentRule = new Rule();
	Command* currentCommand = NULL;
	int expressionOperator = -1;

	rules->push_back(currentRule); // Default rule

	while(!stopParsing) {
		currentToken = nextToken(std::cin);

		switch (currentToken->getType()) {
			case Token::END:
				stopParsing = true;
				runProgram = false;

				for(std::vector<Rule*>::iterator rule = rules->begin(); rule != rules->end(); ++rule) {
					delete (*rule);
				}
				rules->erase(rules->begin(), rules->end());

				break;

			case Token::EOL:
				stopParsing = true;
				break;

			case Token::SEQUENCE:
				currentRule = new Rule();
				rules->push_back(currentRule);

				currentCommand = NULL;
				break;

			case Token::PIPE:
				if(currentCommand != NULL) {
					Command* lastCommand = currentRule->getLastCommand();
					if(dynamic_cast<PipedCommand*>(lastCommand) == 0) {
						int lastExpressionOperator = currentRule->getLastToken(); //MUST HAPPEN BEFORE REMOVING THE LAST COMMAND FROM CURRENT RULE
						currentRule->removeLastCommand();
						Command* pipe = new PipedCommand("Pipe", readPathVariables()); // Command name is not important
						currentRule->addCommand(pipe, lastExpressionOperator);
						dynamic_cast<PipedCommand*>(pipe)->addCommand(lastCommand);
						pipe = NULL;
					}

					currentCommand = NULL;
				} else {
					std::cout << "Syntax error" << std::endl;
				}
				break;

			case Token::INPUT:
				nextTokenIsInput = true;
				break;

			case Token::OUTPUT:
				nextTokenIsOutput = true;
				break;

			case Token::APPEND:
				nextTokenIsOutput = true;
				currentCommand->setAppend(true);
				break;

			case Token::WORD:
				if(nextTokenIsInput) {
					nextTokenIsInput = false;
					currentCommand->setIOInput(currentToken->getText());
				} else if(nextTokenIsOutput) {
					nextTokenIsOutput = false;
					currentCommand->setIOOutput(currentToken->getText());
				} else if(currentCommand == NULL) {
					if(currentToken->getText() == "exit" || currentToken->getText() == "logout") {
						stopParsing = true;
						runProgram = false;

						for(std::vector<Rule*>::iterator rule = rules->begin(); rule != rules->end(); ++rule) {
							delete (*rule);
						}
						rules->erase(rules->begin(), rules->end());

					} else {
						currentCommand = new Command(currentToken->getText(), readPathVariables());
						if(currentCommand->isBuiltInCommand(currentToken->getText().c_str())){
							BuiltInCommand* built = currentCommand->getBuiltInCommmand();
							if(built == NULL){
								std::cout << "what the fuck?!?" << std::endl;
							} else {
								built->setPids(backgroundPids);
							}
						}
						if(currentRule->hasCommands()) {
							Command* lastCommand = currentRule->getLastCommand();
							PipedCommand* pipe = dynamic_cast<PipedCommand*>(lastCommand);
							if(pipe == 0) {
								currentRule->addCommand(currentCommand, expressionOperator);
							} else {
								pipe->addCommand(currentCommand);
							}
						} else {
							currentRule->addCommand(currentCommand, expressionOperator);
						}
					}
				} else {
					currentCommand->addArgument(currentToken->getText());
				}

				break;

			case Token::BACKGROUND:
				currentRule->setBackground();
				break;

			case Token::AND:
				expressionOperator = Token::AND;

				if(currentCommand != NULL) {
					currentCommand = NULL;
				}

				break;

			case Token::OR:
				expressionOperator = Token::OR;

				if(currentCommand != NULL) {
					currentCommand = NULL;
				}

				break;

			case Token::VAR:
				char* var;
				var = getenv(currentToken->getText().c_str());

				if(var != 0) {
					std::cout << var;
				}

				break;
			default:	// missed something ?
				//ofs << "?"<< currentToken->getText() << "?";
				break;
		}

		if(currentToken->getType() != expressionOperator) {
			expressionOperator = -1;
		}

		delete currentToken;    // gooi het token weer weg

	}
	//return currentCommand;
}

void Shell::execute() {
	while(runProgram) {
		std::cout << "UberShell> ";
		parseCommands();

		for(std::vector<Rule*>::iterator rule = rules->begin(); rule != rules->end(); ++rule) {
			runProgram = (*rule)->execute();

			if((*rule)->getIsChildProcess()) runProgram = false;
			else if( ( *rule )->getIsBackground() ) {
				backgroundPids->push_back((*rule)->getChildPid());
			}

			delete (*rule);
		}
		rules->erase(rules->begin(), rules->end());
	}
}

std::vector<std::string>* Shell::readPathVariables() {
	unsigned int temp = 0; // geimplementeerd als controle int vanwege endless loop!!!!
	std::string path = (std::string) getenv("PATH");
	std::vector<std::string>* pathVariables = new std::vector<std::string>;

	unsigned int found = path.find_first_of(":");
	while(found != std::string::npos) {
		if(found > 0) {
			pathVariables->push_back(path.substr(0, found));
		}

		path = path.substr(found + 1);

		found = path.find_first_of(":");
		if(temp == found ){
			//we schieten geen donder op dus endless loop
			//KTNXBAI
			break;
		} else {
			temp = found;
		}
	}

	if(path.length() > 0) {
		pathVariables->push_back(path);
	}

	return pathVariables;
}
